package com.bmbfm.completablefuture.service.impl;

import com.bmbfm.completablefuture.mock.HttpRequestMock;
import com.bmbfm.completablefuture.mock.LogPrint;
import com.bmbfm.completablefuture.mock.PriceResult;
import com.bmbfm.completablefuture.service.ProductComparePriceService;
import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.stream.Stream;

@Service
public class ProductComparePriceServiceImpl implements ProductComparePriceService {

    private ExecutorService threadPool = Executors.newFixedThreadPool(6);

    /**
     * 传统方式【串行】获取多个平台比价信息得到最低价格平台
     */
    @Override
    public PriceResult findCheapestPlatAndPriceBySerial(String productName) {
        // 获取某宝的价格以及优惠
        PriceResult mouBaoPrice = computeRealPrice(HttpRequestMock.getMouBaoPrice(productName),
                HttpRequestMock.getMouBaoDiscounts(productName));
        // 获取某东的价格以及优惠
        PriceResult mouDongPrice = computeRealPrice(HttpRequestMock.getMouDongPrice(productName),
                HttpRequestMock.getMouDongDiscounts(productName));
        // 获取某多多的价格以及优惠
        PriceResult mouDuoDuoPrice = computeRealPrice(HttpRequestMock.getMouDuoDuoPrice(productName),
                HttpRequestMock.getMouDuoDuoDiscounts(productName));
        // 计算并选出实际价格最低的平台
        return Stream.of(mouBaoPrice, mouDongPrice, mouDuoDuoPrice).
                min(Comparator.comparingDouble(PriceResult::getRealPrice))
                .get();
    }

    /**
     * 传统方式通过线程池来增加并发
     */
    @Override
    public PriceResult findCheapestPlatAndPriceByThreadPool(String productName) {
        Future<PriceResult> mouBaoFuture =
                threadPool.submit(() -> computeRealPrice(HttpRequestMock.getMouBaoPrice(productName),
                        HttpRequestMock.getMouBaoDiscounts(productName)));
        Future<PriceResult> mouDongFuture =
                threadPool.submit(() -> computeRealPrice(HttpRequestMock.getMouDongPrice(productName),
                        HttpRequestMock.getMouDongDiscounts(productName)));
        Future<PriceResult> mouDuoDuoFuture =
                threadPool.submit(() -> computeRealPrice(HttpRequestMock.getMouDuoDuoPrice(productName),
                        HttpRequestMock.getMouDuoDuoDiscounts(productName)));
        // 等待所有线程结果都处理完成，然后从结果中计算出最低价
        return Stream.of(mouBaoFuture, mouDongFuture, mouDuoDuoFuture)
                .map(priceResultFuture -> {
                    try {
                        return priceResultFuture.get(5L, TimeUnit.SECONDS);
                    } catch (Exception e) {
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .min(Comparator.comparingDouble(PriceResult::getRealPrice))
                .get();
    }

    /**
     * CompletableFuture并行处理的模式
     */
    @Override
    public PriceResult findCheapestPlatAndPriceByParallel(String productName) {
        // 获取并计算某宝的最终价格
        CompletableFuture<PriceResult> mouBao =
                CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouBaoPrice(productName))
                        .thenCombine(CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouBaoDiscounts(productName)),
                                this::computeRealPrice);
        // 获取并计算某东的最终价格
        CompletableFuture<PriceResult> mouDong =
                CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouDongPrice(productName))
                        .thenCombine(CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouDongDiscounts(productName)),
                                this::computeRealPrice);
        // 获取并计算某多多的最终价格
        CompletableFuture<PriceResult> mouDuoDuo =
                CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouDuoDuoPrice(productName))
                        .thenCombine(CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouDuoDuoDiscounts(productName)),
                                this::computeRealPrice);

        // 排序并获取最低价格
        return Stream.of(mouBao, mouDong, mouDuoDuo)
                .map(CompletableFuture::join)
                .sorted(Comparator.comparingDouble(PriceResult::getRealPrice))
                .findFirst()
                .get();
    }


    /**
     * 演示thenCombine与thenCombineAsync区别，有线程池参与
     */
    public PriceResult getCheapestPlatAndPriceByThreadPoolThenCombineAndThenCombineAsync(String product) {
        // 构造自定义线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);
        return
                CompletableFuture.supplyAsync(
                        () -> HttpRequestMock.getMouDuoDuoPrice(product),
                        executor
                ).thenCombineAsync(
                        CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouDuoDuoDiscounts(product)),
                        this::computeRealPrice,
                        executor
                ).join();
    }

    /**
     * 演示thenCombine与thenCombineAsync区别，无线程池参与
     */
    public PriceResult getCheapestPlatAndPriceByThenCombineAndThenCombineAsync(String product) {
        return
                CompletableFuture.supplyAsync(
                        () -> HttpRequestMock.getMouDuoDuoPrice(product)
                ).thenCombine(
                        CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouDuoDuoDiscounts(product)),
                        this::computeRealPrice
                ).join();
    }

    public void testGetAndJoin(String product) {
        // join无需显式try...catch...
        PriceResult joinResult = CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouDuoDuoPrice(product))
                .join();
        try {
            // get显式try...catch...
            PriceResult getResult = CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouDuoDuoPrice(product))
                    .get(5L, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 演示结合Stream的时候，到底是一个Stream中操作，还是2个Stream中操作，区别
     */
    public PriceResult comparePriceInOnePlat(List<String> products) {
        return products.stream()
                .map(product ->
                        CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouBaoPrice(product))
                                .thenCombine(
                                        CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouBaoDiscounts(product)),
                                        this::computeRealPrice))
                .map(CompletableFuture::join)
                .sorted(Comparator.comparingDouble(PriceResult::getRealPrice))
                .findFirst()
                .get();
    }

    /**
     * 演示两个map写法等同于一个map写法
     */
    public PriceResult comparePriceInOnePlat1(List<String> products) {
        return products.stream()
                .map(product ->
                        CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouBaoPrice(product))
                                .thenCombine(
                                        CompletableFuture.supplyAsync(() -> HttpRequestMock.getMouBaoDiscounts(product)),
                                        this::computeRealPrice)
                                .join())
                .sorted(Comparator.comparingDouble(PriceResult::getRealPrice))
                .findFirst()
                .get();
    }

    // 比价后最终价格计算
    private PriceResult computeRealPrice(PriceResult priceResult, double disCounts) {
        priceResult.setRealPrice(priceResult.getPrice() - disCounts);
        LogPrint.printLog(priceResult.getPlatform() + "最终价格计算完成：" + priceResult.getRealPrice());
        return priceResult;
    }

    // 模拟网络延迟1秒钟
    private String getResult(String result) {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }


}
